// Copyright (c) 2024 刻BITTER
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.


#pragma once

#include "../ioxx_common.hpp"
#include "../ioxx_include_device.hpp"


#if !defined(_IOXX_NVIC_NO_PRIORITY_GROUP) && !defined(_IOXX_NVIC_HAS_PRIORITY_GROUP)

// 只有Cortex-M0 没有中断优先级分组

    #if (__CORTEX_M == 0)

        #define _IOXX_NVIC_NO_PRIORITY_GROUP

    #elif (__CORTEX_M == 3) || (__CORTEX_M == 4) || (__CORTEX_M == 7)

        #define _IOXX_NVIC_HAS_PRIORITY_GROUP

    #else

        #error "priority grouping not specified"

    #endif

#endif


namespace irqxx {

#ifdef _IOXX_NVIC_HAS_PRIORITY_GROUP

    // 中断优先级最多有8 bit

    #if (__NVIC_PRIO_BITS == 2)

    enum class priority_group {
        // NOT PORTABLE
        pre_0_sub_3 = 7,
        pre_1_sub_1 = 6,
        pre_3_sub_0 = 5,

        // PORTABLE
        half_half = pre_1_sub_1,
        no_sub = pre_3_sub_0,
        no_pre = pre_0_sub_3,
        pre_x_sub_1 = pre_1_sub_1
    };

    #elif (__NVIC_PRIO_BITS == 3)

    enum class priority_group {
        // NOT PORTABLE
        pre_0_sub_7 = 7,
        pre_1_sub_3 = 6,
        pre_3_sub_1 = 5,
        pre_7_sub_0 = 4,

        // PORTABLE
        half_half = pre_3_sub_1,
        no_sub = pre_7_sub_0,
        no_pre = pre_0_sub_7,
        pre_x_sub_1 = pre_3_sub_1
    };

    #elif (__NVIC_PRIO_BITS == 4)  // STM32F1 系列有4 个优先级bit

    enum class priority_group {
        // NOT PORTABLE
        pre_0_sub_15 = 7,  // 抢占优先级0 位，中断嵌套功能无效化
        pre_1_sub_7 = 6,   // 抢占可取0-1，响应可取0-7
        pre_3_sub_3 = 5,
        pre_7_sub_1 = 4,
        pre_15_sub_0 = 3,  // 抢占4 位，可取0~15，响应优先级无效

        // PORTABLE
        half_half = pre_3_sub_3,  // 抢占和响应优先级至少都有1 bit（最大值至少为1）
        no_sub = pre_15_sub_0,   // 响应优先级0 位
        no_pre = pre_0_sub_15,   // 抢占优先级0 位，中断嵌套功能无效化
        pre_x_sub_1 = pre_7_sub_1,  // 响应1bit，可取0 或1，剩下的3 位给抢占优先级，可取0 到7
    };

    #elif (__NVIC_PRIO_BITS == 5)

    enum class priority_group {
        // NOT PORTABLE
        pre_0_sub_31 = 7,
        pre_1_sub_15 = 6,
        pre_3_sub_7 = 5,
        pre_7_sub_3 = 4,
        pre_15_sub_1 = 3,
        pre_31_sub_0 = 2,

        // PORTABLE
        half_half = pre_7_sub_3,
        no_sub = pre_31_sub_0,
        no_pre = pre_0_sub_31,
        pre_x_sub_1 = pre_15_sub_1,
    };

    #elif (__NVIC_PRIO_BITS == 6)

    enum class priority_group {
        // NOT PORTABLE
        pre_0_sub_63 = 7,
        pre_1_sub_31 = 6,
        pre_3_sub_15 = 5,
        pre_7_sub_7 = 4,
        pre_15_sub_3 = 3,
        pre_31_sub_1 = 2,
        pre_63_sub_0 = 1,

        // PORTABLE
        half_half = pre_7_sub_7,
        no_sub = pre_63_sub_0,
        no_pre = pre_0_sub_63,
        pre_x_sub_1 = pre_31_sub_1,
    };

    #elif (__NVIC_PRIO_BITS == 7)

    enum class priority_group {
        // NOT PORTABLE
        pre_0_sub_127 = 7,
        pre_1_sub_63 = 6,
        pre_3_sub_31 = 5,
        pre_7_sub_15 = 4,
        pre_15_sub_7 = 3,
        pre_31_sub_3 = 2,
        pre_63_sub_1 = 1,
        pre_127_sub_0 = 0,

        // PORTABLE
        half_half = pre_15_sub_7,
        no_sub = pre_127_sub_0,
        no_pre = pre_0_sub_127,
        pre_x_sub_1 = pre_63_sub_1,
    };

    #elif (__NVIC_PRIO_BITS == 8)

    enum class priority_group {
        // NOT PORTABLE
        pre_0_sub_255 = 8,
        pre_1_sub_127 = 7,
        pre_3_sub_63 = 6,
        pre_7_sub_31 = 5,
        pre_15_sub_15 = 4,
        pre_31_sub_15 = 3,
        pre_63_sub_3 = 2,
        pre_127_sub_1 = 1,
        pre_255_sub_0 = 0,

        // PORTABLE
        half_half = pre_15_sub_15,
        no_sub = pre_255_sub_0,
        no_pre = pre_0_sub_255,
        pre_x_sub_1 = pre_127_sub_1
    };

    #else

    #endif

    //template <priority_group G>
    //constexpr uint32_t lowest_preempt_priority() {
    //    auto n = _IOXX_ENUM_TO_UNDERLYING(G);
    //    auto preempt_n = __NVIC_PRIO_BITS - n - 1;
    //    return (1 << preempt_n) - 1;  // n 个二进制位，最大能表示的数字是2^n - 1
    //}


    //template <priority_group G>
    //constexpr uint32_t lowest_sub_priority() {
    //    auto n = _IOXX_ENUM_TO_UNDERLYING(G);
    //    auto lsb_bit_count = (8 - __NVIC_PRIO_BITS);
    //    auto sub_n = n + 1;

    //    if constexpr (sub_n <= lsb_bit_count) {
    //        return 0;
    //    }
    //    else {
    //        return (1 << (sub_n - lsb_bit_count)) - 1;
    //    }
    //}


    inline void setup_priority_group(priority_group g) {
        __NVIC_SetPriorityGrouping(_IOXX_ENUM_TO_UNDERLYING(g));
    }


    class IrqInit {
       private:
        // 默认初始值：启用中断（ENABLE）
        uint8_t _preempt = 0;
        uint8_t _sub = 0;


       public:
        IrqInit &preemption_priority(uint8_t p) {
            _preempt = p;
            return *this;
        }

        IrqInit &sub_priority(uint8_t p) {
            _sub = p;
            return *this;
        }

        IrqInit &enable(IRQn_Type n) {
            uint32_t group = __NVIC_GetPriorityGrouping();
            uint32_t pri = NVIC_EncodePriority(group, _preempt, _sub);
            NVIC_SetPriority(n, pri);
            NVIC_EnableIRQ(n);
            return *this;
        }

        IrqInit &disable(IRQn_Type n) {
            NVIC_DisableIRQ(n);
            return *this;
        }
    };


#elif defined(_IOXX_NVIC_NO_PRIORITY_GROUP)

    constexpr auto lowest_preempt_priority() {
        return 0xff >> (8 - __NVIC_PRIO_BITS);  // n 个二进制位，最大能表示的数字是2^n - 1
    }


    class IrqInit {
       private:
        // 默认初始值：启用中断（ENABLE）
        uint8_t _preempt = 0;


       public:
        IrqInit &preemption_priority(uint8_t p) {
            _preempt = p;
            return *this;
        }

        IrqInit &enable(IRQn_Type n) {
            NVIC_SetPriority(n, _preempt);
            NVIC_EnableIRQ(n);
            return *this;
        }

        IrqInit &disable(IRQn_Type n) {
            NVIC_DisableIRQ(n);
            return *this;
        }
    };

#else

    #error "either _IOXX_NVIC_NO_PRIORITY_GROUP or _IOXX_NVIC_HAS_PRIORITY_GROUP must be defined"

#endif


    inline void enable_irq() {
        __enable_irq();
    }


    inline void disable_irq() {
        __disable_irq();
    }


    inline void enable_irq(IRQn_Type irqn) {
        NVIC_EnableIRQ(irqn);
    }


    inline void disable_irq(IRQn_Type irqn) {
        NVIC_DisableIRQ(irqn);
    }


    // /**
    //  * @brief 一个ScopeGuard，临时关闭所有指定优先级以下的中断
    //  *
    //  */
    // class PriorityDeafScope {
    //    public:
    //     explicit PriorityDeafScope(uint8_t banned_priority) {
    //         // TODO:
    //         disable_interrupt();
    //     }

    //     ~PriorityDeafScope() {
    //         enable_interrupt();
    //     }
    // };




    /**
     * @brief 一个ScopeGuard，用来暂时关闭所有中断
     *
     */
    class CriticalScope {
        private:
        uint32_t _reg;
        
       public:
        explicit CriticalScope() {
            _reg = __get_PRIMASK();
            __disable_irq();
        }

        ~CriticalScope() {
            __set_PRIMASK(_reg);
        }
    };


}  // namespace irqxx